import base64
import datetime
import io
import queue
import re
import threading
import time
import tkinter as tk
from tkinter import messagebox, filedialog
from tkinter import ttk
import pygame
import pyttsx3



class FlashTypingApp:
    def __init__(self, root):
        self.engine = pyttsx3.init()
        self.engine.setProperty('rate', 200)
        self.speech_queue = queue.Queue()  # 新增：用于管理语音任务的队列
        self.speech_thread = threading.Thread(target=self.process_speech_queue, daemon=True)
        self.speech_thread.start()  # 启动语音处理线程
        self.root = root
        self.root.title("打字练习")
        # 初始化pygame混音器
        pygame.mixer.init()
        self.load_embedded_sound()
        self.default_text1 = """
book [书] ruler [尺子] pencil [铅笔] eraser [橡皮擦] pencil_case [铅笔盒]
backpack [背包] school [学校] eye [眼睛] hand [手] ear [耳朵]
mouth [口] nose [鼻子] foot [脚] face [面] leg [腿]
arm [臂] animals [动物] cat [猫] bird [鸟] rabbit [兔子]
dog [狗] chicken [鸡] duck [鸭子] monkey [猴子] tiger [老虎]
panda [熊猫] elephant [大象] fish [鱼] one [一] two [二]
three [三] four [四] five [五] six [六] seven [七]
eight [八] nine [九] ten [十] colour [颜色] red [红色]
yellow [黄色的] purple [紫色] brown [棕色的] orange [橙色] white [白色]
green [绿色] pink [粉红色] blue [蓝色] black [黑色] apple [苹果]
banana [香蕉] peach [桃子] melon [甜瓜] pear [梨] grape [葡萄]
strawberry [草莓] pineapple [菠萝] classroom [教室] door [门] window [窗口]
blackboard [黑板] wall [墙] desk [书桌] chair [椅子] boy [男孩]
girl [女孩] in [在里面] on [在] under [在下面] where [哪里]
room [房间] closet [壁橱] telephone [电话] computer [计算机] TV [电视]
bed [床] picture [照片] table [桌子] lamp [灯] armchair [扶手椅]
behind [后面] next_to [在旁边] toy [玩具] plane [飞机] boat [船]
train [火车] ball [球] teddy_bear [泰迪熊] bus [公共汽车] car [汽车]
doll [玩偶] pinwheel [风车] box [箱] shape [形状] circle [圆圈]
triangle [三角形] rectangle [矩形] square [广场] eleven [十一] twelve [十二]
thirteen [十三] fourteen [十四] fifteen [十五] sixteen [十六] seventeen [十七]
eighteen [十八] nineteen [十九] twenty [二十] clothes [衣服] T-shirt [T恤衫]
pants [裤子] shorts [短裤] jacket [夹克] sweater [毛衣] skirt [裙子]
dress [连衣裙] shoe [鞋] sock [短袜] food [食物] drink [喝]
rice [大米] noodles [面条] jiaozi [饺子] tofu [豆腐] vegetable [蔬菜]
meat [肉] bread [面包] milk [牛奶] ice_cream [冰淇淋] juice [果汁]
egg [鸡蛋] saland [萨兰德] hamburger [汉堡包] family [家庭] grandpa [爷爷]
grandma [奶奶] dad [爸爸] mom [妈妈] sister [姐妹] brother [兄弟]
me [我] policeman [警察] doctor [医生] teacher [老师] engineer [工程师]
classmate [同学] friend [朋友] big [大的] thin [薄的] pretty [漂亮的]
ugly [丑陋的] tall [高的] short [短的] from [从] China [中国]
America [美国] Canada [加拿大] Britain [英国] park [停车场] hill [小山]
lake [湖] bridge [桥] tree [树] grass [草] flower [花]
bee [蜜蜂] butterfly [蝴蝶] bench [长凳] beautiful [美丽的] aunt [阿姨]
uncle [叔叔] street [街道] van [厢式货车] taxi [出租车] bicycle [自行车]
subway [地铁] subway_station [地铁站] traffic_light [交通灯] bus_stop [公共汽车站] supermarket [超市]
book_store [书店] hospital [医院] zoo [动物园] stop [停止] wait [等待]
Beijing_Opera [京剧] Temple_Fair [庙会] great [伟大的] festival [节日] merry [愉快的]
Christmas [圣诞节] Christmas_tree [圣诞树] Santa_Claus [圣诞老人] bell [钟] present [目前]
card [卡片] stocking [长袜] happy [幸福的] Spring_Festival [春节] dragon_dance [龙舞]
                """
        self.default_text2 = """
paper-cut [剪纸] lantern [灯笼] Hong_Kong [香港] Toronto [多伦多] Sydney [悉尼]
rat [老鼠] ox [公牛] dragon [龙] snake [蛇] horse [马]
sheep [羊] rooster [公鸡] pig [猪] New_Year [新年] sofa [沙发]
season [季节] spring [春天] summer [夏天] fall [落下] winter [冬天]
bright [明亮的] golden [金色的] plant [植物] swim [游泳] climb [攀登]
make [制作] snowman [雪人] fly [飞] kite [风筝] sleep [睡觉]
weather [天气] sunny [晴朗的] cloudy [多云的] rainy [多雨的] windy [多风的]
snowy [下雪的] cold [寒冷的] cool [凉爽的] warm [温暖的] hot [热的]
day [白天] scarf [围巾] sunglasses [太阳镜] coat [外套] like [喜欢]
today [今天] raincoat [雨衣] umbrella [雨伞] PE [PE公司] class [班]
can [可以] throw [扔] jump [跳] catch [抓住] walk [步行]
run [运行] kick [踢] bounce [反弹] very_well [非常好] play [玩]
pingpong [乒乓球] rope [绳] football [足球] can't [不能] swing [摆动]
seesaw [跷跷板] slide [滑动] line_up [整队] at_ease [轻松地] attention [注意]
count_off [报数] turn_left [向左转] turn_right [向右转] time [时间] o'clock [点]
It's_time_for... [是时候了] breakfast [早餐] lunch [午餐] supper [晚饭] minute [分钟]
plus [加] half [一半] hour [小时] thirty [三十] forty [四十]
fifty [五十] sixty [六十] second [第二] get_up [起床] wash [洗]
brush [刷] teeth [牙齿] watch [看] go_to_bed [去睡觉] when [什么时候]
every_day [每天] at [在] week [周] Monday [星期一] Tuesday [星期二]
Wednesday [星期三] Thursday [星期四] Friday [星期五] Saturday [星期六] Sunday [星期日]
weekday [平日] weekend [周末] don't [不要] want [希望] Chinese [中国人]
music [音乐] math [数学] art [艺术] am [上午] pm [项目经理]
year [年] month [月] myself [我自己] new [新的] name [名称]
Ms [女士] Mr. [先生] old [古老的] phone [电话] number [数]
primary_school [小学] body [身体] head [头] hair [头发] finger [手指]
neck [脖子] feet [脚] monster [怪物] hurt [伤害] toe [脚趾]
matter [问题] bad [坏的] feel [感觉] fridge [冰箱] beef [牛肉]
mantou [馒头] sausage [香肠] meatball [肉丸] pancake [煎饼] sandwich [三明治]
tomato [番茄] potato [马铃薯] carrot [胡萝卜] some [一些] pet [宠物]
turtle [乌龟] parrot [鹦鹉] goldfish [金鱼] tail [尾] smart [聪明的]
cute [可爱的] Catch_a_mouse [捉老鼠] Wag_its_tail [摇尾巴] cap [帽子] vest [背心]
gloves [手套] jeans [牛仔裤] sneakers [运动鞋] wear [穿] birthday [生日]
January [一月] February [二月] March [三月] April [四月] May [五月]
June [六月] July [七月] August [八月] September [九月] October [十月]
November [十一月] December [十二月] what [什么] you [你] your [你的]
bike [自行车] are [是] tea [茶] long [长的] small [小的]
have [有] lion [狮子] who [谁] delicious [美味的] fruit [水果]
talk [谈话] sing [唱] shirt [衬衫] nice [美好的] thanks [谢谢]
he [他] his [他的] please [拜托] library [图书馆] computer_room [计算机机房]
                        """
        self.default_text3 = """
washroom [洗手间] clinic [诊所] office [办公室] music_room [音乐室] first [第一]
third [第三的] floor [地板] gym [健身房] art_room [艺术室] science_room [科学室]
between [之间] English [英语] hard [坚硬的] easy [容易的] fun [乐趣]
boring [无聊的] interesting [有趣的] favourite [最喜欢的] why [为什么] after_school [放学后]
living_room [起居室] dining_room [餐厅] kitchen [厨房] bedroom [卧室] study [学习]
bathroom [浴室] balcony [阳台] garage [车库] people [人员] grandfather [祖父]
grandmother [祖母] cousin [表妹] cook [烹调] waiter [服务员] dentist [牙医]
listen_to_music [听音乐] walk_the_dog [遛狗] grandparents [祖父母] invitation [邀请] crayon [蜡笔]
scissors [剪刀] pen [钢笔] glue [胶] paper [纸张] party [缔约方]
get_ready [准备好] draw [画] cut [切] stick [粘贴] write [写]
next [下一个] then [然后] finally [最后] with [具有] practice [实践]
sport [运动] game [游戏] jogging [慢跑] hiking [徒步旅行] tennis [网球]
cycling [骑自行车] hockey [曲棍球] baseball [棒球] also [也] sometimes [有时]
often [经常] concert [音乐会] go_to_the_movies [去看电影] Lego [乐高] drive [开车]
bumper_car [碰碰车] puzzle [令人费解的事] exciting [令人兴奋的] just [只是] so-so [一般]
play_with_fire [玩火] downstairs [楼下] touch [触摸] careful [仔细的] dangerous [危险的]
sidewalk [人行道] underpass [地下通道] footbridge [人行天桥] crosswalk [人行横道] fence [栅栏]
use [使用] safe [安全的] cross [交叉] climb_over [翻越] rule [规则]
pass [通过] help [帮助] dictionary [词典] math_problem [数学问题] CD_player [激光唱机]
mailman [邮递员] salesclerk [销售员] hairdresser [理发师] computer_programmer [计算机程序编制员] singer [歌手]
post_office [邮局] store [商店] hotel [酒店] beauty_shop [美容店] building [建筑]
bank [银行] grocery_store [杂货店] drugstore [药店] bookstore [书店] police_station [警察局]
bakery [面包房] hairdresser's [理发店] restaurant [餐厅] go_straight [往前走] crossing [交叉路口]
forward [向前地] city [城市] museum [博物馆] gallery [画廊] university [大学]
collect_stickers [收集贴画] make_models [制作模型] sew [缝补] knit [编织] collect_coins [收集硬币]
travel [旅行] hike [徒步旅行] communication [交流，联络] write_a_letter [写信] send_an_e-mail [发送电子邮件]
cell_phone [手机] her [她] their [他们的] twins [双胞胎] blond [金发碧眼]
curly [卷曲的] slim [苗条的] straight [直的] freckles [雀斑] talkative [健谈的]
honest [诚实] careless [粗心的] popular [流行的] hardworking [努力工作] shy [害羞的]
lazy [懒惰的] quiet [安静的] helpful [有益的] friendly [友好的] funny [有趣的]
than [比] better [更好的] fast [快速的] thick [厚的] best [最好的]
notebook [笔记本] stapler [订书机] glue_stick [胶棒] disk [磁盘] whiteout [白化]
folder [文件夹] sticker [贴纸] expensive [昂贵的] cheap [便宜的] a_pair_of [一对]
them [他们] fit [适合] oil [油] bowl [碗] plate [盘子]
wok [炒锅] crack [裂纹] mix [混合] put [放] add [添加]
salt [盐] serve [服务] stir-fry [炒菜] stir-fried_vegetables [炒蔬菜] cucumber [黄瓜]
soy_sauce [酱油] sauce [酱] vinegar [醋] sugar [糖] pepper [胡椒粉]
children [儿童] show [显示] report [报告] entertainment [娱乐] nature [自然]
Peking_opera [京剧] commercial [商业的] movie [电影] kind_of [种类] think_of [考虑]
                        """
        self.default_text4 = """
channel [通道] schedule [日程安排] comedy [喜剧] video [视频] action [行动]
keep_fit [保持健康] tired [疲劳的] plenty_of [大量] before [在...之前] exercise [锻炼]
toothache [牙痛] more [更多] candy [糖果] medicine [医学] take_some_medicine [吃点药]
junk_food [垃圾食品] righe_after [刚什么就] do_homework [做作业] visit [参观] last [最后的]
stay [停留] row [行] yesterday [昨天] all_day [整天] wonderful [精彩的]
lose [失去] terrible [可怕的] buy [购买] souvenir [纪念品] Inner_Mongolia [内蒙古]
ride [骑] teach [教] meet [满足] whole [整体] far_away [遥远的]
noisy [嘈杂的] nursery_school [托儿所] rhyme [押韵] become [成为] Young_Pioneer [少先队员]
win [赢] prize [奖] competition [竞争] province [省份] east [东]
Russian [俄语] Japanese [日语] pyramid [金字塔] koala [树袋熊] kangaroo [袋鼠]
reptile [爬行动物] mammal [哺乳动物] insect [昆虫] crocodile [鳄鱼] ostrich [鸵鸟]
penguin [企鹅] whale [鲸] shark [鲨鱼] hippo [河马] zebra [斑马]
hummingbird [蜂鸟] centimeter [厘米] giraffe [长颈鹿鹿] meter [米] kilometer [千米:公里]
strong [强壮的] wing [翅膀] ton [吨] heavy [重的] surprised [惊讶的]
angry [愤怒的] worried [担心的] sad [悲哀的] excited [兴奋的] nervous [焦虑的]
proud [自豪的] artist [艺术家] actor [演员] musician [音乐家] writer [作家]
scientist [科学家] inventor [发明家] German [德国的] Danish [丹麦语] American [美国人]
compose [组成] national_anthem [国歌] shrimp [虾] invent [发明] light_bulb [灯泡]
story [故事] astronaut [宇航员] detective [侦探] professor [教授] president [总统]
principal [主要的] sun [太阳] moon [月亮] ocean [海洋] cloud [云]
robot [机器人] housework [家务活] pollution [污染] pill [药丸] peace [和平]
earth [地球] spaceship [宇宙飞船] Mars [火星] attend [出席] software [软件]
develop [发展] basic [基本的] personal [个人的] leader [领导] knock [敲]
quickly [迅速地] nut [坚果] claw [爪] lock [锁] common [常见的]
moose [驼鹿] modern [现代的] teeny_tiny [微小的] make_sure [确保] litter_box [垃圾箱]
sick [生病的] bored [无聊的] push [推] everywhere [到处] poor [贫穷的]
fantasy [幻想] reason [原因] sign [签名] photography [摄影技术] fire [火]
bloom [开花] temperature [温度] degree [度] during [在期间] seed [种子]
return [返回] example [例子] wake_up [唤醒] such_as [例如] disappearing [正在消失]
cost [成本] however [然而] decide [决定] sail [帆] sailor [水手]
finish [完成] alone [单独地] Germany [德国] beauty [美女] magic [魔术]
agree [同意] follow [跟随] continue [持续] machine [机器] lift [举起]
hold [持有] sense [感觉] outer_space [外层空间] volcano [火山] construct [建造]
wizard [向导] suddenly [突然] hit [打] sly [狡猾的] cave [洞穴]
a [一] act [行为] and [和] bag [袋] close [关闭]
down [向下] fine [好的] for [对于] good [好的] I [我]
learn [学习] morning [早晨] my [我的] open [打开] stand [站]
take [拿] thank [感谢] to [到] up [向上的] give [给]
many [许多的] this [这] taste [味道] wave [波动] is [是的]
it [它] lemon [柠檬] no [不] not [不] that [那个]
                        """
        self.default_text5 = """
the [这个] autumn [秋天] bean [豆] leaf [叶] look [看]
mooncake [月饼] taro [芋头] father [父亲] get [得到] mother [母亲]
balloon [气球] little [小的] tongue [舌头] tongue-twister [绕口令] fold [折叠]
try [尝试] very [非常] well [好] do [做] frog [青蛙]
colour [颜色] out [外面的] fireman [消防队员] goodbye [再见] milkman [送奶工]
turn [转] all [全部的] listen [听] tell [告诉] farmer [农民]
fat [脂肪] fisherman [渔夫] they [他们] coke [焦炭] ice-cream [冰淇淋]
jelly [果冻] eat [吃] noodle [面条] bin [垃圾箱] road [路]
biscuit [饼干] hot_dog [热狗] water [水] ferry [渡轮] light [光]
slow [缓慢的] star [明星] glove [手套] wind [风] snow [雪]
belt [带] hen [母鸡] chick [小鸡] cow [奶牛] bear [熊]
quilt [被子] rubber [橡胶] sweet [甜的] skip [跳过] dirty [肮脏的]
towel [毛巾] soap [肥皂] toothbrush [牙刷] toothpaste [牙膏] glass [玻璃]
fork [叉] knife [刀] spoon [勺子] chopsticks [筷子] X-ray [X射线]
shell [壳] sand [沙] clock [时钟] night [夜] dinner [晚餐]
how [怎样] see [看见] clean [清洁的] jam [果酱] late [晚的]
mouse [鼠标] bow [弓] queen [女王] rain [雨] she [她]
we [我们] clap [鼓掌] hall [大厅] playground [操场] toilet [厕所]
shape [形状] point [指向] gate [大门] ladybird [瓢虫] root [根]
camera [照相机] photograph [照片] grey [灰色] mask [面具] mirror [镜子]
spin [旋转] spinner [微调器] toothpick [牙签] cup [杯子] dot [点]
drill [训练] hole [孔] join [参加] roof [屋顶] rough [粗糙的]
smooth [光滑的] soft [软的] toy [玩具] prawn [对虾] smell [气味]
sour [酸的] lizard [蜥蜴] button [按钮] foil [箔片] skateboard [滑板]
super [超级的] wolf [狼] mountain [山] rainbow [彩虹] sea [海]
sky [天空] violet [紫罗兰] lorry [卡车] loud [大声的] noise [噪音]
radio [收音机] raindrop [雨滴] television [电视] grow [成长] nest [巢]
shine [闪耀] wet [潮湿的] dive [潜水] paint [油漆] read [阅读]
bring [带来] engine [发动机] man [男人] policewoman [女警察] postman [邮递员]
shop_assistant [车间助理] knee [膝] round [圆] shoulder [肩] full [满的]
brave [勇敢的] sharp [锋利的] hoop [环箍] butter [黄油] chocolate [巧克力]
coffee [咖啡] crisp [酥脆的] flour [面粉] large [大的] feed [喂养]
fountain [喷泉] pick [挑选] pond [池塘] sketch-book [素描本] corn [玉米]
hay [干草] leave [离开] rubbish [垃圾] stone [石] its [它的]
log [日志] moth [蛾类] stalk [茎秆] trunk [大旅行箱] question [问题]
felt_pen [毛毡笔] those [那些] beside [在旁边] drum [鼓] each [每个]
guitar [吉他] piano [钢琴] recorder [录音机] violin [小提琴] donkey [驴]
sit [坐] blind [失明的] fox [狐狸] grape [葡萄] lemon_juice [柠檬汁]
lime_juice [酸橙汁] orange_juice [橙汁] shop [商店] dig [挖掘] mop [拖把]
shake [摇] dining-room [餐厅] rock [岩石] sitting-room [起居室] soldier [士兵]
                        """
        self.default_text6 = """
dish [盘] dry [干的] homework [家庭作业] letter [信]
motorbike [摩托车] pilot [飞行员] student [学生] afternoon [下午] collect [收集]
evening [傍晚] near [近的] temple [寺庙] top [顶部] thunder [打雷]
back [后面] duckling [小鸭] now [现在] swan [天鹅] smoke [烟]
station [站] list [列表] picnic [野餐] tape [磁带] vegetable [蔬菜]
but [但是] dear [亲爱的] start [开始] term [学期] uniform [制服]
home [家] outside [外部] dessert [甜点] know [知道] nod [点头]
shout [呼喊] cupboard [橱柜] every [每一个] fan [风扇] hundred [一百]
lesson [课程] maths [数学] Physical [物理的] quarter [季度] science [科学]
subject [主题] timetable [时间表] tram [有轨电车] money [钱] note [笔记]
cocoon [蚕茧] feeler [测隙规] lay [放置] spell [拼写] diver [潜水员]
dolphin [海豚] garden [花园] island [岛] place [地方] seal [密封]
tower [塔] rise [上升] in_front_of [在前面] or [或] glasses [眼镜]
flat [平的] shadow [阴影] cherry [樱桃] tie [领带] awake [唤醒]
string [一串] musical [音乐的] wheel [轮] asleep [睡着的] shaker [振动筛]
hammer [铁锤] screwdriver [螺丝刀] over [结束] instrument [工具] lady [女士]
empty [空的] mine [矿] move [移动] parcel [包裹] cushion [垫]
yours [你的] fluffy [毛茸茸的] castle [城堡] same [相同的] goose [鹅]
save [节约] rich [富有的] beanstalk [豆茎] sell [卖] giant [巨人]
different [不同的] lost [丢失的] step [步] ask [问] dinosaur [恐龙]
puppet [木偶] key [钥匙] constable [警察] shelf [架子] rug [小地毯]
roll [卷] tadpole [蝌蚪] were [是] hers [她的] score [分数]
goal [目标] shower [淋浴] build [建造] age [年龄] millimetre [毫米]
always [总是] estate [不动产] member [成员] only [只有] right [正确的]
usually [通常] young [年轻的] centimetre [厘米] cycle [周期] dress_up [打扮一下]
puppy [小狗] share [分享] skate [滑冰] together [在一起] at_the_weekend [在周末]
weight [重量] get_to [到达] loudly [大声地] love [爱] pelican [鹈鹕]
pitch [抛] quietly [安静地] slowly [缓慢地] soon [很快] space [空间]
toucan [巨嘴鸟] fire-engine [消防车] job [工作] put_out [熄灭] spacecraft [航天器]
get_on [上] traffic_jam [交通阻塞] litter [乱丢杂物垃圾] litter_bin [废物箱] lot [许多]
minibus [小型公共汽车] pavement [铺装层] post [邮递] lamp_post [灯柱] post_box [邮箱信箱]
traffic [交通] left [左边] way [方式] on_one's_way_to [往什么去的路上] wrong [错误的]
zebra_crossing [斑马线] carton [纸箱] cheese [奶酪] crab [螃蟹] dairy_product [乳制品]
frozen [冻结] lemonade [柠檬水] lettuce [莴苣] onion [洋葱] product [产品]
section [部分] snacks [小吃] spend [花费] squid [乌贼] steak [牛排]
cereals [谷物] choose [选择] congee [粥] ever [曾经] fried [油炸的]
menu [菜单] salad [沙拉] soup [汤] soya_milk [豆奶] sundae [圣代]
sushi [寿司] toast [干杯] chicken_wing [鸡翅] diet [饮食] enough [足够地]
ham [火腿] healthy [健康的] a_little [一点] piece [块] a_piece_of [一块]
pizza [比萨饼] slice [片] a_slice_of [一片什么] soft_drink [软饮料] think [认为]
                        """
        self.default_text7 = """
oven [炉] peel [剥离] shopping [购物] tin [锡] arrive [到达]
basketball [篮球] compass [罗盘] end [结束] except [除了] leap_year [闰年]
map [地图] needle [针] noon [正午] railway [铁路] rest [休息]
Chinese_New_Year [春节] forecast [预测] holiday [假日] relative [相对的] river [河]
sandal [凉鞋] ticket [票] trip [旅行] go_on_a_trip [继续旅行] will [将]
special [特殊的] thousand [千] thousands [千] high_jump [跳高] model [模型]
stamp [邮票] table-tennis [乒乓球] volleyball [排球] be_born [出生] feather [羽毛]
few [很少的] a_few [有几个] fight [战斗] length [长度] pin [大头针]
quite [相当地] run_away [逃跑] sword [剑] weigh [权衡] because [因为]
begin [开始] boot [靴子] frightening [吓人的] heavy_rain [大雨] laugh [笑]
lie [躺] sandcastle [沙堡] have_a_swim [游泳] builder [建造商] change [改变]
factory [工厂] fishing [钓鱼] harbour [港湾] history [历史] hundreds_of [数百个]
million [百万] other [其他] town [镇] village [村庄] worker [工人]
adult [成人] child [小孩] secondary_school [中学] teenager [青少年] work [工作]
berry [浆果] charcoal [木炭] cigarette [香烟] cooked [煮熟的] countryside [乡村]
danger [危险] drop [滴] electricity [电] gas [气体] heat [热]
helicopter [直升机] match [比赛] millions_of [数百万的] out_of [在什么外] pack [包装]
poster [招贴] quick [快] raw [未经加工的] some_more [更多] son [儿子]
still [仍然] talk_about [谈论] without [没有] wood [木材] air [空气]
alive [活着的] breathe [呼吸] burn [燃烧] closed [已关闭] construction [建设]
corner [角] cry [哭泣] district [区] dust [灰尘] gently [轻轻地]
handkerchief [手帕] happen [发生] heavily [沉重地] high [高的] keep [保持]
kilometre [公里数] metre [米] object [对象] parachute [降落伞] per [根据]
power_station [发电站] stay_up [熬夜] bath [洗澡] broken [破碎的] land [土地]
litre [升] madam [夫人] most [最] narrow [狭窄的] pipe [管]
plastic [塑料] pump [泵] reservoir [蓄水池] seaweed [海藻] sincerely [真诚地]
sir [先生] tap [水龙头] waterfall [瀑布] wide [宽的] answer [回答]
diamond [钻石] fax [传真] forest [森林] kill [杀死] part [零件]
pollute [污染] remember [记得] send [发送] skin [皮肤] starry [繁星点点]
stream [流动] twinkle [闪烁] wink [眨眼] wonder [想知道] world [世界]
daughter [女儿] dead [死去的] else [其他的] granddaughter [孙女] grandson [孙子]
nephew [侄子] niece [侄女] able [能够的] botanical [植物学] call [呼叫]
environment [环境] find_out [找出] get_it [明白] if [如果] kind [友善的]
tell_a_lie [说谎] might [可以] others [其他] pick_up [拣起] playtime [游戏时间]
promise [承诺] keep_a_promise [守信用] solve [解决] someone [某人] sum [总和]
tonight [今晚] very_much [非常地] yet [然而] badminton [羽毛球] idea [主意]
mall [购物中心] market [市场] somewhere [在某处] businessman [商人] businesswoman [女商人]
clerk [店员] project [项目] secretary [秘书] would [将] board [板]
choir [唱诗班] club [俱乐部] covered [已覆盖] entrance [入口] information [信息]
invite [邀请] parent [父级] programme [方案] staff [工作人员]
                        """
         # weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
        self.week= self.get_current_weekday()
        if self.week=="星期一":
            self.practice_text = self.default_text1
        if self.week=="星期二":
            self.practice_text = self.default_text2
        if self.week=="星期三":
            self.practice_text = self.default_text3
        if self.week=="星期四":
            self.practice_text = self.default_text4
        if self.week=="星期五":
            self.practice_text = self.default_text5
        if self.week=="星期六":
            self.practice_text = self.default_text6
        if self.week=="星期日":
            self.practice_text = self.default_text7
        self.current_pos = 0
        self.skip_chars = {' ', '\t', '\n'}
        self.correct_count = 0
        self.current_char = ""
        self.blink_id = None
        self.flash_id = None
        self.blink_state = False
        # 记录练习开始时间
        self.start_time = time.time()
        # 设置全屏
        self.root.attributes('-fullscreen', True)
        self.root.bind("<F11>", self.toggle_fullscreen)
        self.root.bind("<Escape>", self.exit_fullscreen)
        self.root.bind("<Key>", self.handle_key_press)
        # 创建界面
        self.setup_ui()

    def get_current_weekday(self):
        """获取当前星期几"""
        weekdays = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
        today = datetime.datetime.now().weekday()  # 获取当前日期的星期索引
        return weekdays[today]
    # 键盘音效
    def load_embedded_sound(self):
        sound_base64 = """
        SUQzAwAAAABCMVREQVQAAAAFAAAAMTIwM1RJTUUAAAAFAAAAMTQ1N1BSSVYAABj3AABYTVAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS41LWMwMjEgNzkuMTU1MjQxLCAyMDEzLzExLzI1LTIxOjEwOjQwICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iCiAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICB4bWxuczp4bXBETT0iaHR0cDovL25zLmFkb2JlLmNvbS94bXAvMS4wL0R5bmFtaWNNZWRpYS8iCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpiZXh0PSJodHRwOi8vbnMuYWRvYmUuY29tL2J3Zi9iZXh0LzEuMC8iCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OWI0MWNhNTMtNTM4Yi00Y2ViLTk3YjctZmE1YzI3Y2EzZmZkIgogICB4bXBNTTpEb2N1bWVudElEPSJlMTM1YjgwNS1kMjMzLTQwZWYtZTM1OC1iZWQ0MDAwMDAwNDkiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNGVlMTgyNi05N2MwLTRiZjgtOTU4Yi0xNjJmMDExMzIwMjIiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMTQtMDMtMTJUMTQ6NTc6NTEtMDQ6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDE0LTAzLTEyVDE0OjU3OjUxLTA0OjAwIgogICB4bXA6Q3JlYXRlRGF0ZT0iMjAxNC0wMy0xMlQxNDo1NzoyMS0wNDowMCIKICAgeG1wRE06YXVkaW9TYW1wbGVSYXRlPSI0NDEwMCIKICAgeG1wRE06YXVkaW9TYW1wbGVUeXBlPSIxNkludCIKICAgeG1wRE06YXVkaW9DaGFubmVsVHlwZT0iU3RlcmVvIgogICB4bXBETTpzdGFydFRpbWVTY2FsZT0iMzAwMDAiCiAgIHhtcERNOnN0YXJ0VGltZVNhbXBsZVNpemU9IjEwMDEiCiAgIGRjOmZvcm1hdD0iTVAzIgogICBiZXh0OmRlc2NyaXB0aW9uPSJPRkZJQ0UgQ09NUFVURVIgS0VZQk9BUkQgUFJFU1MgU0lOR0xFIEtFWSBIQVJEIDAxIgogICBiZXh0Om9yaWdpbmF0b3I9IkFkb2JlIFN5c3RlbXMgSW5jIgogICBiZXh0Om9yaWdpbmF0aW9uRGF0ZT0iMjAxNC0wMy0wNSIKICAgYmV4dDpvcmlnaW5hdGlvblRpbWU9IjIwOjAyOjM0IgogICBiZXh0OnRpbWVSZWZlcmVuY2U9IjAiCiAgIGJleHQ6dmVyc2lvbj0iMSI+CiAgIDx4bXBNTTpIaXN0b3J5PgogICAgPHJkZjpTZXE+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSIzOTNkODlmYy00YzI1LTZlZWItOWQxYi0yMGMxMDAwMDAwNzYiCiAgICAgIHN0RXZ0OndoZW49IjIwMTQtMDMtMTJUMTQ6NTc6NTEtMDQ6MDAiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIEFkb2JlIE1lZGlhIEVuY29kZXIgQ0MgKE1hY2ludG9zaCkiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9IjMzNmM3Y2Q0LWJmMTQtODNkOS1iMzE0LWM5OWUwMDAwMDA3NiIKICAgICAgc3RFdnQ6d2hlbj0iMjAxNC0wMy0wNVQyMDowMjozNC0wNTowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciBDQyAoTWFjaW50b3NoKSIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpDMjRDQ0YwRTk1MjA2ODExOTEwOUIzQjFEMUZCNUQxNCIKICAgICAgc3RFdnQ6d2hlbj0iMjAxMy0wNC0zMFQxMTo0MToyMC0wNDowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciA1LjUuMCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iL21ldGFkYXRhOy9jb250ZW50Ii8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjFCMUNDODI0OTkyMDY4MTE5MTA5QjNCMUQxRkI1RDE0IgogICAgICBzdEV2dDp3aGVuPSIyMDEzLTA0LTMwVDEyOjEwOjM1LTA0OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIDUuNS4wIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249Im1vZGlmaWVkIgogICAgICBzdEV2dDpwYXJhbWV0ZXJzPSJ1bmtub3duIG1vZGlmaWNhdGlvbnMiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9ImJkZDc2N2M1LWVjZmYtNGFiNS03ZTgxLTZhMzQwMDAwMDA3NiIKICAgICAgc3RFdnQ6d2hlbj0iMjAxNC0wMy0wNVQxOTozNDowNi0wNTowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciBDQyAoTWFjaW50b3NoKSIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo4ZjQxMjU1OS1iZWY1LTQ3MjgtYWU5Mi1jYzFhNDdjMjMyNWQiCiAgICAgIHN0RXZ0OndoZW49IjIwMTQtMDMtMDVUMjA6MDI6MzQtMDU6MDAiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIEFkb2JlIE1lZGlhIEVuY29kZXIgQ0MgKE1hY2ludG9zaCkiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6M2Y5NzMyNTAtMDNmOS00YjhhLTgyNDUtODA3Y2QzMjc4NjQwIgogICAgICBzdEV2dDp3aGVuPSIyMDE0LTAzLTA1VDIwOjAyOjM0LTA1OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIENDIChNYWNpbnRvc2gpIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvbWV0YWRhdGEiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MGFiMTlhNzgtNmYzMi00YmU4LTg1NWYtYjY5MzkxODI2MGFlIgogICAgICBzdEV2dDp3aGVuPSIyMDE0LTAzLTEyVDE0OjU3OjUxLTA0OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBBZG9iZSBNZWRpYSBFbmNvZGVyIENDIChNYWNpbnRvc2gpIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvIi8+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InNhdmVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjliNDFjYTUzLTUzOGItNGNlYi05N2I3LWZhNWMyN2NhM2ZmZCIKICAgICAgc3RFdnQ6d2hlbj0iMjAxNC0wMy0xMlQxNDo1Nzo1MS0wNDowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgQWRvYmUgTWVkaWEgRW5jb2RlciBDQyAoTWFjaW50b3NoKSIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iL21ldGFkYXRhIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICAgPHhtcE1NOkRlcml2ZWRGcm9tCiAgICBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjNmOTczMjUwLTAzZjktNGI4YS04MjQ1LTgwN2NkMzI3ODY0MCIKICAgIHN0UmVmOmRvY3VtZW50SUQ9IjI5ZTA5MTU5LTc1NWYtZjA0MC0zZjRlLWFmMzYwMDAwMDA0OSIKICAgIHN0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3MGQ4OTFjYi0xN2RkLTRjOGEtOGJhMS0xMDg0Mjk0NTBiODkiLz4KICAgPHhtcERNOnN0YXJ0VGltZWNvZGUKICAgIHhtcERNOnRpbWVGb3JtYXQ9IjI5OTdEcm9wVGltZWNvZGUiCiAgICB4bXBETTp0aW1lVmFsdWU9IjAwOzAwOzAwOzAwIi8+CiAgIDx4bXBETTphbHRUaW1lY29kZQogICAgeG1wRE06dGltZVZhbHVlPSIwMDswMDswMDswMCIKICAgIHhtcERNOnRpbWVGb3JtYXQ9IjI5OTdEcm9wVGltZWNvZGUiLz4KICAgPHhtcERNOmR1cmF0aW9uCiAgICB4bXBETTp2YWx1ZT0iNCIKICAgIHhtcERNOnNjYWxlPSIxMDAxLzMwMDAwIi8+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4AVElUMwAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKVRQVUIAAAAYAAAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSlXT0FSAAAAFwAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSlURU5DAAAAGAAAANW+s6TL2LLEKHNjLmNoaW5hei5jb20pVEFMQgAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKVRZRVIAAAAFAAAAMjAyMVRQRTIAAAAYAAAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSlUSVQyAAAAGAAAANW+s6TL2LLEKHNjLmNoaW5hei5jb20pVENPTgAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKUNPTU0AAAAcAAAAY2hpANW+s6TL2LLEKHNjLmNoaW5hei5jb20pVFBFMQAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/++BAAAALMWlEdTGAArstKI6mMABc7hdx2PeAA53C7jse8ACLqJIRIib7/wAAOAwTn7RIEh00uvfscEg8bAgOjJLJ7RIJlSQTObXr7/l/YcqdiWT7CWrOCZEOYA4VpgAwHqiQIAkHnXWddf9FizqUmcoscbEg8qdmZmv+j/0pzZ2vxe+wscbMz+x2fvzSlMpf6Odde/i9ffGFixzVjm3vjDmrHG17+LFmnB51169e/i+9Fjmr1/3v87e/0Xv/RYscptKTf5pSZ295osc04ckP90B0cPzeIjlg/gA7///Ef///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////xdRJCJETff+AABwGCc/aJAkOml179jgkHjYEB0ZJZPaJBMqSCZza9ff8v7DlTsSyfYS1ZwTIhzAHCtMAGA9USBAEg866zrr/osWdSkzlFjjYkHlTszM1/0f+lObO1+L32FjjZmf2Oz9+aUplL/Rzrr38Xr74wsWOasc298Yc1Y42vfxYs04POuvXr38X3osc1ev+9/nb3+i9/6LFjlNpSb/NKTO3vNFjmnDkh/ugOjh+bxEcsH8AHf//4jYXmLGKENzFBIppppq8ki4J+YJyCvj8L4JmXghZ2iuEkNMmamIAPx4cJPI5doiGOCpb3aOZ2nfhuagiItiMcvJkEFvv34SMY55rKOMklJgnAoNa/3RKHW1KBXN5HRnjnEUH1n/+GiTTaTcH5dKJc/0wwKyJ////tYZ9vV50a5wQI6jV6smhK1WLi+NU//+srVmNTqPwY8Nj1L2FnvH3vEVe3v/7////7Ym2PatjKN0xLDaXNsKc5BXqtsdOJhSqtRx1ZMPSzHd//////////////mLelpsb+8bz9fW/////5L39YObSwIbC8xYxQhuYoJFNNNNXkkXBPzBOQV8fhfBMy8ELO0VwkhpkzUxAB+PDhJ5HLtEQxwVLe7RzO078NzUERFsRjl5Mggt9+/CRjHPNZRxkkpME4FBrX+6JQ62pQK5vI6M8c4ig+s//w0SabSbg/LpRLn+mGBWRP///9rDPt6vOjXOCBHUavVk0JWqxcXxqn//1lasxqdR+DHhsepews94+94ir29//f////bE2x7VsZRumJYbS5thTnIK9VtjpxMKVVqOOrJh6WY7v/////////////8xb0tNjf3jefr63/////Je/rBzaWBD/++BAAAALFldY7mMAAMvK6x3MYAAcFZ9VvYwAO4Kz6rexgAcpEsvFsmIlkslkstqRVMdYNfBgIuC0mkuseE7rjMlU4UbaY16Lqea0rC15rzWYOdp/puMWqGG41PF3oYkhdWvq5LqbOt4OEPGCiVio3UUHTVaNZ6u40wIS2zsQG9S+ZTnhqrS97+vyLyFqVbEggaRONkO62Vf8f3+Xd69wpuW4tZh1wkNqb/y/eP465////vUqazIW5O06MFNep8pT+suZZb/X///vX//P9I1TJQJlz7L5QCsCi75M+LZF6otSlXJVItTkPEw4JPlRUFf////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8pEsvFsmIlkslkstqRVMdYNfBgIuC0mkuseE7rjMlU4UbaY16Lqea0rC15rzWYOdp/puMWqGG41PF3oYkhdWvq5LqbOt4OEPGCiVio3UUHTVaNZ6u40wIS2zsQG9S+ZTnhqrS97+vyLyFqVbEggaRONkO62Vf8f3+Xd69wpuW4tZh1wkNqb/y/eP465////vUqazIW5O06MFNep8pT+suZZb/X///vX//P9I1TJQJlz7L5QCsCi75M+LZF6otSlXJVItTkPEw4JPlRUFSSUm243G2k4n8mc05ToBKL/IPM2lrcZCslUMUXytlShr7rLRch5l4yas+t2jootfU14gmau1mGXlmpG/LlSe9AVlQ0vCBBl6WuoMqWy6ZdmclNSG3dpKtDEXFlN9yYpNyaxDUP3s3Fp357Ua0u6Fy5d0AwmUS2UxmIOVP3J7uVqrO4zNmlw5lbrWrmr8RlztX4e5GZU/z/a3j+WVLjjWr3rku7hlrUZucuV5qNQ9cm525DW3Sdaaxna1rvKupVjO25VWtTU3hWl0ajVNVlMVu5dpbMamcaWGY1Kp7mseVqZCSSk23G420nE/kzmnKdAJRf5B5m0tbjIVkqhii+VsqUNfdZaLkPMvGTVn1u0dFFr6mvEEzV2swy8s1I35cqT3oCsqGl4QIMvS11BlS2XTLszkpqQ27tJVoYi4spvuTFJuTWIah+9m4tO/PajWl3QuXLugGEyiWymMxByp+5PdytVZ3GZs0uHMrda1c1fiMudq/D3IzKn+f7W8fyypcca1e9cl3cMtajNzlyvNRqHrk3O3Ia26TrTWM7Wtd5V1KsZ23Kq1qam8K0ujUapqspit3LtLZjUzjSwzGpVPc1jytTIT/++BAAAAP+mXP6e97bv3Muf09723PLMMjrGGLYeWYZHWMMWwMlJuSNyNpJizBFHaWwBJJkBiPJUiSklHqV47gEokIpJPkIH2EZWRNQbJqokTUuSqOI5WAq0aX4/lCrVS+J0fxCx6WVDiQhcvzuABwvgNUvD9DUafx1tB5k5PUI8cyDbxbTpQ1gseyuQpmQ5CorUPphVhbkKKEsMZ+xGAUzi5xnuoUGWLlUvs1xaj6NWWumtlrBYSUqhyVrDNTONyvf8RcWxebxvJukbEKzE5WtVhcW6+dPbY38b/alFd7EzTMSCrcY76Nu1Yr2L4Us3+JYtR0Vf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wyUm5I3I2kmLMEUdpbAEkmQGI8lSJKSUepXjuASiQikk+QgfYRlZE1BsmqiRNS5Ko4jlYCrRpfj+UKtVL4nR/ELHpZUOJCFy/O4AHC+A1S8P0NRp/HW0HmTk9QjxzINvFtOlDWCx7K5CmZDkKitQ+mFWFuQooSwxn7EYBTOLnGe6hQZYuVS+zXFqPo1Za6a2WsFhJSqHJWsM1M43K9/xFxbF5vG8m6RsQrMTla1WFxbr509tjfxv9qUV3sTNMxIKtxjvo27VivYvhSzf4li1HRV///////////////////////////////////////////////////////////8JN3bWyRpAApBIlhYabwvYQjLfpdpNpgAgBdhI4UGoKichEqJgKPL+sid6hf2I2s7sMxmN5wC6tJjOXMTo2IqhONN3i6VW3R9bZraNvd4kutWpzHZ65dLtZttaQNUYz896BdZ62KP+14pLN6e23Q03DqVPf/S/26TdYSbu2tkjSABSCRLCw03hewhGW/S7SbTABAC7CRwoNQVE5CJUTAUeX9ZE71C/sRtZ3YZjMbzgF1aTGcuYnRsRVCcabvF0qtuj62zW0be7xJdatTmOz1y6Xazba0gaoxn570C6z1sUf9rxSWb09tuhpuHUqe/+l/t0m6z/++BAAAAP+T5Iaw9C4X8nyQ1h6FwHHFsZjDBHCOOLYzGGCOERq2WySSRttplIHJrIChrKZaJUYVVLTR5gbbpaH8fpUGCYBlEGMfEPDS5wnzapm5agYYomM10tbdEmgSFqDwPgbSdanIOC1iEHrCLuMDtYHMh6qPGWvX1VlbVPxcV/V//cf9xH5KFUVuCrhUWWCrziHzyjxGdqW5JmPdLJrOOwaK//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////iNWy2SSSNttMpA5NZAUNZTLRKjCqpaaPMDbdLQ/j9KgwTAMogxj4h4aXOE+bVM3LUDDFExmulrbok0CQtQeB8DaTrU5BwWsQg9YRdxgdrA5kPVR4y16+qsrap+Liv6v/7j/uI/JQqitwVcKiywVecQ+eUeIztS3JMx7pZNZx2DRX///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////bjTTJTKgAw5pKlKHAM0ITKvkLq1YHx7G6JJyp6a2nBOFHNFVQCsOGiiAwQZUJqN/poVF8bw76ta43GmmSmVABhzSVKUOAZoQmVfIXVqwPj2N0STlT01tOCcKOaKqgFYcNFEBggyoTUb/TQqL43h31a1z/++BAAAAP/ABFhQAACBoACLCgAAEdaiVP+PmQC61Eqf8fMgH////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8MaMLUMUrM2MFConHI7HZHI2VWSAug+zjGOLAdZRw2yot5YycHcOYQ1MV8ZcaI4zYlSJlwZ8hhYHCmaGZuKXIoVJQSRJ/d2JIVoLgKAz4zAoAWQGry+7+NMZgfYyZHiC4cGDYgMsEeAIQDQJ33x7HeTBOGxomAVQBUYuYYwiQ5ArTZrbFxkyoTBUTdMMFlIliACAggAAUcQetvtfNXQUya3QPkwMuUTcdZEz44ERlxbBSfZDbbaaEUKhfJxZuX0mL6adQeuFzgyiZFxlyuXzcUvNS+JQHZ////7f///9Y5g5hUN1IOgyabm4eIwxowtQxSszYwUKiccjsdkcjZVZIC6D7OMY4sB1lHDbKi3ljJwdw5hDUxXxlxojjNiVImXBnyGFgcKZoZm4pcihUlBJEn93YkhWguAoDPjMCgBZAavL7v40xmB9jJkeILhwYNiAywR4AhANAnffHsd5ME4bGiYBVAFRi5hjCJDkCtNmtsXGTKhMFRN0wwWUiWIAICCAABRxB62+181dBTJrdA+TAy5RNx1kTPjgRGXFsFJ9kNttpoRQqF8nFm5fSYvpp1B64XODKJkXGXK5fNxS81L4lAdn////t////1jmDmFQ3Ug6DJpubh4j/++BAAAALFWzWdj3gAMotms7HvAAcMYMvPYyAK4YwZeexkAU0YnlElWYkcQFVVWqatCRgkKHEPQqSYvRwqMnBmjxMMkS7FxNMn6Fkgci4RWxlTNXadOVh2pToUBCIlYzXuNRqLe4pNOKlTMTeXo/WXHbuaMJeXLcm1Ku3Sl2htfnO8Ky6pVsLntTt2bT2z//8lST0qTkQjDSz1UrKfsOLAis0H+vrq38RwiqN+uWplix49s0s+/zqC9//+v/8u52Y6lloiNimSymfW0fySrBxBZVLiM12zXHr9//////1tGjemL73rDcH3/sPPwaI///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////5oxPKJKsxI4gKqqtU1aEjBIUOIehUkxejhUZODNHiYZIl2LiaZP0LJA5FwitjKmau06crDtSnQoCERKxmvcajUW9xSacVKmYm8vR+suO3c0YS8uW5NqVdulLtDa/Od4Vl1SrYXPanbs2ntn//5KknpUnIhGGlnqpWU/YcWBFZoP9fXVv4jhFUb9ctTLFjx7ZpZ9/nUF7//9f/5dzsx1LLREbFMllM+to/klWDiCyqXEZrtmuPX7//////raNG9MX3vWG4Pv/Yefg0RWWb9mvrVucJ/2GpiBdMvWaIgwS4jO25NqwEyFTMZs/7aMjbI2RH5nT/MPgeYcGQKUtcceca7DNhZLsw4giMUYwzDLEXK3s4ypl0Wa0waUJtnsaXxW4oMo3By0W0cp1ZXNyaXrtTlX42KH52ljshm7aYqRyXS8qNynSc7GM1GnQc+7iQ3ecFpqt0fUy6uaXNahUndqNTMOymdmoelMegb5TYzx3Kd50XdWrW5m1lEp7Va/Vpb+qtLZy3zWNy1+VW9KZTlj81Lrt7LKrS4483V1+ONmUxnXzVrKVU25DjcsUTJZZZv2a+tW5wn/YamIF0y9ZoiDBLiM7bk2rATIVMxmz/toyNsjZEfmdP8w+B5hwZApS1xx5xrsM2FkuzDiCIxRjDMMsRcrezjKmXRZrTBpQm2expfFbigyjcHLRbRynVlc3Jpeu1OVfjYofnaWOyGbtpipHJdLyo3KdJzsYzUadBz7uJDd5wWmq3R9TLq5pc1qFSd2o1Mw7KZ2ah6Ux6BvlNjPHcp3nRd1atbmbWUSntVr9Wlv6q0tnLfNY3LX5Vb0plOWPzUuu3ssqtLjjzdXX442ZTGdfNWspVTbkONyxRMlj/++BAAAAP+TZG6xjC0ZymyN1jGFoHzHMZBgSpAFeOYyDAlSBQlJyNtuNIkoTkUUFmcCMQdIKBQJBAkeS2qtoQmncFlqyQMWGYaZSmLAtecd6AZLepcbOL6vy0qKP1QsNdXvGVMSZzJp6rhKWcxmAntBB4ZkMTRNQAwI1pYVsCVzYZ2lmYze+rZwyrV39fagn7Ea7q/j+WOWdJPb/uXbOGWXfqmnogYHYdWwO6Z5NXKuyKV/uh1Z2qv1f/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////qEpORttxpElCciigszgRiDpBQKBIIEjyW1VtCE07gstWSBiwzDTKUxYFrzjvQDJb1LjZxfV+WlRR+qFhrq94ypiTOZNPVcJSzmMwE9oIPDMhiaJqAGBGtLCtgSubDO0szGb31bOGVau/r7UE/YjXdX8fyxyzpJ7f9y7Zwyy79U09EDA7Dq2B3TPJq5V2RSv90OrO1V+r//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+pUAEBMBJVhlHASnphJ//////+JAY3//////////////////////////////////////////////////////UqACAmAkqwyjgJT0wk///////EgMb/++BAAAAP/ABLgAAACZyACXAAAAEKgAEuAAAAIAAAJcAAAAT/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////++BAAAAP/ABLgAAACZyACXAAAAEKgAEuAAAAIAAAJcAAAAT///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9UQUfVvrOky9iyxChzYy5jaGluYXouY29tKSAgICAgICDVvrOky9iyxChzYy5jaGluYXouY29tKSAgICAgICDVvrOky9iyxChzYy5jaGluYXouY29tKSAgICAgICAyMDIx1b6zpMvYssQoc2MuY2hpbmF6LmNvbSkgICAgICAgDA==
        """
        try:
            sound_data = base64.b64decode(sound_base64)
            sound_file = io.BytesIO(sound_data)
            self.key_sound = pygame.mixer.Sound(sound_file)
        except Exception as e:
            print(f"无法加载内置音效: {e}")
            self.key_sound = pygame.mixer.Sound(buffer=bytearray(0))

    def setup_ui(self):
        """设置用户界面"""
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True)
        style = ttk.Style()
        style.theme_use('default')
        style.configure("Horizontal.TProgressbar",
                        troughcolor="#eeeeee",
                        background="#4CAF50",
                        thickness=20)
        # 控制按钮框架
        control_frame = ttk.Frame(main_frame)
        control_frame.pack(fill=tk.X, pady=(0, 40))
        # 控制按钮
        ttk.Button(
            control_frame,
            text="加载文本",
            command=self.load_text_file,
            style="Accent.TButton"  # 使用强调按钮样式
        ).pack(side=tk.LEFT, padx=20)
        # 状态信息
        self.status_label = ttk.Label(
            main_frame,
            text="按下键盘开始练习...",
            font=("Microsoft YaHei", 30),  # 增大字体
            foreground="#FF5722"  # 使用醒目的橙色
        )
        self.status_label.pack(pady=(0, 20))
        # 文本预览区域
        self.text_preview = tk.Text(
            main_frame,
            height=20,
            wrap=tk.WORD,
            font=("Consolas", 15),  # 增大字体
            state=tk.DISABLED,
            bg="#ffffff",
            relief=tk.SOLID,
            borderwidth=2
        )
        self.text_preview.pack(fill=tk.BOTH, expand=True, padx=20, pady=(0, 20))
        self.text_preview.tag_config("current_char", background="pink")
        self.text_preview.tag_config("correct", foreground="green", font=("Consolas", 18, "bold"))
        self.text_preview.tag_config("wrong", foreground="red", font=("Consolas", 18, "bold"))
        self.text_preview.tag_config("flash", background="red")
        # self.text_preview.tag_config("error_highlight", background="yellow")
        self.text_preview.tag_config("comment", foreground="green", font=("Consolas", 14))
        # 初始化文本显示
        self.update_text_preview()
        self.start_blinking()

    def start_blinking(self):
        """开始闪烁当前字符的下划线"""
        if self.blink_id:
            self.root.after_cancel(self.blink_id)
        if self.current_pos < len(self.practice_text):
            self.blink_state = not self.blink_state
            if self.blink_state:
                self.text_preview.tag_config("current_char", underline=True, underlinefg="red")
            else:
                self.text_preview.tag_config("current_char", underline=False)
            self.blink_id = self.root.after(500, self.start_blinking)

    def flash_error(self):
        """显示错误闪烁"""
        if self.flash_id:
            self.root.after_cancel(self.flash_id)
            self.text_preview.tag_remove("flash", "1.0", tk.END)
        start_index = f"1.0 + {self.current_pos} chars"
        self.text_preview.tag_add("flash", start_index, f"{start_index} + 1 chars")
        self.flash_id = self.root.after(1000, lambda: self.text_preview.tag_remove("flash", "1.0", tk.END))

    def reset_practice(self):
        """重置练习状态"""
        self.current_pos = 0  # 重置当前字符位置
        self.update_text_preview()  # 更新文本预览
        self.status_label.config(text="按下键盘开始练习...", foreground="gray")  # 重置状态标签

    def handle_key_press(self, event):
        """处理键盘按键"""
        self.play_key_sound(event)
        if self.current_pos == len(self.practice_text):
            self.status_label.config(text="练习完成！", foreground="green")
            end_time = time.time()
            elapsed_time = end_time - self.start_time
            seconds = int(elapsed_time)
            # 我像把seconds处理成分秒的形式
            minutes, seconds = divmod(seconds, 60)
            print(f"本次打字用了 {minutes} 分钟 {seconds} 秒")
            threading.Thread(target=self.speak_word, args=(f"本次打字用了 {minutes} 分钟 {seconds} 秒",)).start()
            return
        # 跳过注释行
        if self.practice_text[self.current_pos] == '#':
            self.current_pos = self.skip_comment_line()
            self.update_text_preview()
            return
        target_char = self.practice_text[self.current_pos]
        if target_char in self.skip_chars:
            self.current_pos += 1
            self.update_text_preview()
            return
        if event.char and event.char == target_char:
            self.mark_correct()
        elif event.char:
            self.mark_wrong(target_char)
        self.update_text_preview()

    # 新增辅助方法
    def skip_comment_line(self):
        """跳过注释行"""
        while self.current_pos < len(self.practice_text) and self.practice_text[self.current_pos] != '\n':
            self.current_pos += 1
        if self.current_pos < len(self.practice_text) and self.practice_text[self.current_pos] == '\n':
            self.current_pos += 1
        return self.current_pos

    def mark_correct(self):
        """标记为正确输入"""
        start_index = f"1.0 + {self.current_pos} chars"
        self.text_preview.tag_add("correct", start_index, f"{start_index} + 1 chars")
        self.correct_count += 1
        self.current_pos += 1
        self.status_label.config(text="正确！继续...", foreground="green")
        # 检查是否输入了一个完整的单词
        if self.current_pos < len(self.practice_text) and self.practice_text[self.current_pos] in self.skip_chars:
            word_end = self.current_pos
            word_start = word_end
            while word_start > 0 and self.practice_text[word_start - 1] not in self.skip_chars:
                word_start -= 1
            word = self.practice_text[word_start:word_end]
            # print(f"输入了一个完整的单词：{word}")
            word = re.sub(r'_', ' ', word)
            # threading.Thread(target=self.speak_word, args=(word,)).start()
            # 利用正则表达式帮我找出word_end后的出现的第一个[]中间的字符串
            match = re.search(r'\[([^]]*)\]', self.practice_text[word_end:])
            if match:
                 # print(f"[]中间的字符串：{match.group(1)}")
                 self.current_pos = word_end + match.end()
                 read = word +" "+match.group(1)
                 threading.Thread(target=self.speak_word, args=(read,)).start()
                 # time.sleep(1)

            while self.current_pos < len(self.practice_text) and self.practice_text[
                self.current_pos] in self.skip_chars:
                self.current_pos += 1
            if self.current_pos >= len(self.practice_text):
                self.status_label.config(text="练习完成！", foreground="green")
    def mark_wrong(self, target_char):
        """标记为错误输入"""
        self.flash_error()
        start_index = f"1.0 + {self.current_pos} chars"
        self.text_preview.tag_add("wrong", start_index, f"{start_index} + 1 chars")
        self.status_label.config(text=f"错误！应该是 '{target_char}'", foreground="red")
    def play_key_sound(self, event):
        """播放按键音效"""
        if event.char and event.keysym not in ["BackSpace", "Delete", "Shift_L", "Shift_R", "Control_L", "Control_R"]:
            try:
                self.key_sound.play()
            except:
                pass

    def update_text_preview(self):
        """更新文本预览"""
        self.text_preview.config(state=tk.NORMAL)
        self.text_preview.delete(1.0, tk.END)
        self.text_preview.insert(tk.END, self.practice_text)
        # 高亮当前字符
        if self.current_pos < len(self.practice_text):
            while self.current_pos < len(self.practice_text) and self.practice_text[
                self.current_pos] in self.skip_chars:
                self.current_pos += 1
            if self.current_pos < len(self.practice_text):
                start_index = f"1.0 + {self.current_pos} chars"
                self.text_preview.tag_add("current_char", start_index, f"{start_index} + 1 chars")
                self.text_preview.see(start_index)
        # 标记注释行
        lines = self.practice_text.split('\n')
        line_start = 0
        for line in lines:
            if line.strip().startswith('#'):
                start_index = f"1.0 + {line_start} chars"
                end_index = f"{start_index} + {len(line)} chars"
                self.text_preview.tag_add("comment", start_index, end_index)
            line_start += len(line) + 1
        self.text_preview.config(state=tk.DISABLED)

    def process_speech_queue(self):
        """处理语音任务队列"""
        while True:
            word = self.speech_queue.get()
            if word is None:  # 队列结束标志
                break
            self.engine.say(word)
            self.engine.runAndWait()

    def speak_word(self, word):
        """异步调用的函数,用于朗读单词"""
        self.speech_queue.put(word)

    def load_text_file(self):
        """加载文本文件"""
        file_path = filedialog.askopenfilename(
            title="选择文本文件",
            filetypes=[("文本文件", "*.txt"), ("Python文件", "*.py"), ("所有文件", "*.*")]
        )
        if file_path:
            try:
                with open(file_path, "r", encoding="utf-8") as f:
                    self.practice_text = f.read()
                self.reset_practice()
                messagebox.showinfo("成功", "文本加载成功！")
                threading.Thread(target=self.speak_word, args=("文本加载成功",)).start()
                self.file_path = file_path  # 保存文件路径
            except Exception as e:
                messagebox.showerror("错误", f"无法加载文件:\n{str(e)}")

    def toggle_fullscreen(self, event=None):
        """切换全屏模式"""
        self.root.attributes('-fullscreen', not self.root.attributes('-fullscreen'))

    def exit_fullscreen(self, event=None):
        """退出全屏模式"""
        self.root.attributes('-fullscreen', False)
        threading.Thread(target=self.speak_word, args=("退出全屏模式",)).start()

    def __del__(self):
        """析构函数,关闭数据库连接并清理语音队列"""
        self.speech_queue.put(None)  # 结束语音处理线程
        self.speech_thread.join()


    def run(self):
        """运行应用程序"""
        self.root.mainloop()


if __name__ == "__main__":
    root = tk.Tk()
    app = FlashTypingApp(root)
    app.run()

# pyinstaller --onefile --noconsole --icon=打字游戏.ico  --name "格格打字 V 3.0.0"  英语打字2.py
